﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UDDL.Model;
using System.Diagnostics;

namespace UDDL.Parser
{
	public class Parser
	{
		public Parser(String filename, String source)
		{
			DataDescription = new DataDescription();
			m_filename = filename;
			Tokenizer tokenizer = new Tokenizer(filename, source);
			m_tokenStream = tokenizer.TokenStream;
			ParseRoot();
		}

		void ParseRoot()
		{
			ParseNamespace(DataDescription.Root, 0);
		}

		void ParseNamespace(Namespace ns, int indent)
		{
			while (true)
			{
				int oldPosition = m_currentPosition;

				switch (CurrentToken.Type)
				{
					case TokenType.EndOfStream:
						return;
					case TokenType.NewLine:
						if (NewLine(indent))
							return;
						break;
					case TokenType.Word:
						switch (CurrentToken.Text.ToLower())
						{
							case "namespace":
								++m_currentPosition;

								ParseNamespace(ns.GetNamespace(AcceptSymbol()), indent + 1);
								break;
							case "entity":
								++m_currentPosition;
								ParseEntity(ns.GetEntity(AcceptSymbol()), indent + 1);
								break;
						}
						break;
				}

				if (oldPosition == m_currentPosition)
					ThrowError("Unexpected token");
			}
		}

		void ParseEntity(Entity entity, int indent)
		{
			while (true)
			{
				int oldPosition = m_currentPosition;

				switch (CurrentToken.Type)
				{
					case TokenType.EndOfStream:
						return;
					case TokenType.NewLine:
						if (NewLine(indent))
							return;
						break;
					case TokenType.Word:
						switch (CurrentToken.Text.ToLower())
						{
							case "entity":
								{
									++m_currentPosition;
									//ParseEntity(new Entity(AcceptSymbol()), indent + 1);
								}
								break;
							case "property":
								{
									++m_currentPosition;
									String typeName = AcceptSymbol();
									ParseProperty(entity.AddProperty(AcceptSymbol(), typeName), indent + 1);
								}
								break;
						}
						break;
				}

				if (oldPosition == m_currentPosition)
					ThrowError("Unexpected token");
			}
		}

		void ParseProperty(Property property, int indent)
		{
			SkipLine();
		}

		void SkipLine()
		{
			while (CurrentToken.Type != TokenType.NewLine && CurrentToken.Type != TokenType.EndOfStream)
				++m_currentPosition;
		}

		String AcceptSymbol()
		{
			if (CurrentToken.Type != TokenType.Word)
				ThrowError(String.Format("Symbol expected {0} found", CurrentToken.Type));
			String result = CurrentToken.Text;
			++m_currentPosition;
			return result;
		}

		bool NewLine(int indent)
		{
			if (Token(1).Type == TokenType.EndOfStream)
				return true;
			if (Token(1).Type == TokenType.Indent)
			{
				if (Token(1).Text.Length > indent)
					ThrowError("Indentation error");
				if (Token(1).Text.Length < indent)
					return true;
				m_currentPosition += 2;
				return false;
			}
			else
			{
				return indent == 0;
			}
		}

		void ThrowError(String message)
		{
			throw new ParserError(String.Format("{0}({1}:{2}): {3}", m_filename, CurrentToken.Line, CurrentToken.Position, message));
		}

		Token CurrentToken
		{
			get
			{
				return m_tokenStream[m_currentPosition];
			}
		}

		Token Token(int n)
		{
			return m_tokenStream[m_currentPosition + n];
		}

		public DataDescription DataDescription { get; private set; }
		private List<Token> m_tokenStream;
		private String m_filename;
		private int m_currentPosition = 0;

		class ParserError : Exception
		{
			public ParserError(String message)
				: base(message)
			{
			}
		}
	}
}
